Skip to content

fix: re-sign a pending EHF signal on every block so a failed first attempt can recover - #8129

Closed
PastaPastaPasta wants to merge 1 commit into
dashpay:developfrom
PastaPastaPasta:fix/ehf-resign
Closed

PastaPastaPasta wants to merge 1 commit into
dashpay:developfrom
PastaPastaPasta:fix/ehf-resign

Conversation

@PastaPastaPasta

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

A masternode signs an EHF signal (DIP-0023) exactly once per quorum. Its vote is persisted, and the sig shares of an incomplete signing session are dropped 60 seconds after the last new share. So if fewer than the threshold of the selected quorum's members sign within the same minute, the signal is never signed: later blocks hit "already voted ... Not voting again", and a restart doesn't help. Nothing retries until a different quorum is selected for the request.

That is what happened to the v24 signal on mainnet. When its window opened, too few members of the selected llmq_400_85 quorum had upgraded. The ones that upgraded later each signed alone, their shares timed out, and the signal has stayed unsigned even though well over the threshold of that quorum now runs v24.

What was done?

CEHFSignalsHandler::trySignEHFSignal now passes allowReSign=true, so every member of the selected quorum signs again on each new block tip while the signal is unmined, its window is open and no recovered signature exists. A block where enough members are online then completes the signature.

Re-signing is a re-send of the same share: the message hash depends only on the version bit and the selected quorum, BLS signatures are deterministic, and the persisted vote is not rewritten. It stops once a recovered signature exists or the signal is mined. InstantSend's retroactive signing already uses the same flag.

Both connection modes were traced and tested:

  • Without all-members-connected (mainnet's llmq_400_85): after the timeout the share is created and announced again; before it, ForceReAnnouncement re-queues the known shares.
  • With all-members-connected: ForceReAnnouncement returns early, but signing again resets the member's recovery session, so the share goes to the recovery members again.

Peers that don't have this change handle the repeats without penalty: sessions are keyed by sign hash, so a repeated announcement reuses the existing session entry, shares the receiver already holds are dropped before queuing, and nothing on that path scores the sender.

Note for a mixed network: a member without this change still signs only once per quorum, so the signal recovers once the threshold of the selected quorum's members run the fix, or when the selected quorum changes.

How Has This Been Tested?

feature_mnehf.py now uses a 3-of-3 quorum: two members are isolated when the signal becomes signable, the remaining share times out (61 s of mocktime) and another block is mined, the members reconnect, and one more block must produce the EHF transaction. The test asserts that the selected quorum is the same before and after, so the recovery comes from re-signing. A --spork21 variant covers the all-members-connected mode.

  • Without the source change, both variants fail with EHF signal did not recover after retrying with the same quorum; with it, both pass.
  • feature_mnehf.py (both variants), feature_llmq_signing.py (both variants), feature_llmq_chainlocks.py, feature_llmq_is_retroactive.py and feature_asset_locks.py pass.
  • Unit tests llmq*, evo*, versionbits* pass (257 cases).
  • Debug logs from the test runs show the same sign hash and message hash before the timeout and after re-creation, the share going out again in each mode, and no further shares once the signal is mined.

Breaking Changes

None. While an EHF signal is pending, each member of the selected quorum produces one sig share per block for the EHF quorum type.

Checklist:

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone (if you have permission to set one)

🤖 Generated with Claude Code

Allow members to re-sign pending EHF signals after incomplete signing sessions expire. Exercise recovery with the same quorum after isolation and timeout in both connection modes.
@PastaPastaPasta PastaPastaPasta added this to the 24.1 milestone Nov 12, 2026
@PastaPastaPasta PastaPastaPasta changed the title fix(llmq): re-sign a pending EHF signal on every block so a failed first attempt can recover fix: re-sign a pending EHF signal on every block so a failed first attempt can recover Nov 12, 2026
@coderabbitai

coderabbitai Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

trySignEHFSignal now passes true for both boolean arguments to AsyncSignIfMember. The functional test adds a partial-quorum timeout and retry scenario, expands its setup to four nodes and three LLMQs, and runs with SPORK 21 both disabled and enabled.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • dashpay/dash#7778: This PR changes the quorum setup and mining helpers in the same functional test that #7778 introduced.
Merge Risk: Minimal · up to b619a

This change lets a pending EHF signal be re-signed after a failed first attempt, and it adds tests for that recovery. No merge-blocking code risk was found. The PR title scope "llmq" fails the title check and should be changed to an allowed scope.

Pre-merge checks: Passed 4

Check name Status Explanation
Description check Passed The description explains the problem (EHF signal signed once per quorum, shares dropped after timeout), the fix (allowReSign=true so members re-sign each block while the signal is unmined), the testin…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Title check Passed The title states the main change: the EHF signal is re-signed on every block, so a failed first signing attempt can recover. The raw summary shows the code change to trySignEHFSignal passing true for …
Full details: Description check

Explanation

The description explains the problem (EHF signal signed once per quorum, shares dropped after timeout), the fix (allowReSign=true so members re-sign each block while the signal is unmined), the testing (feature_mnehf.py with a 3-of-3 quorum, both connection modes, unit tests), and the mixed-network caveat. It is directly related to the changeset, which includes the ehf_signals.cpp change and the feature_mnehf.py and test_runner.py updates.

Full details: Title check

Explanation

The title states the main change: the EHF signal is re-signed on every block, so a failed first signing attempt can recover. The raw summary shows the code change to trySignEHFSignal passing true for AsyncSignIfMember, and the test changes exercise recovery after a timeout. The title matches the changeset and the stated objectives. It uses the conventional fix: prefix and is 86 characters, slightly long but clear.

Finishing Touches

Generate unit tests (beta)

  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

Recent review info

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f26aa35f-fe5a-4916-a173-0cd0fee86f06
Commits

Reviewing files that changed from the base of the PR and between bc71be6 and b619a4f.

Files selected for processing: 3

  • src/llmq/ehf_signals.cpp
  • test/functional/feature_mnehf.py
  • test/functional/test_runner.py

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.

@thepastaclaw

thepastaclaw commented Nov 12, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit b619a4f) · triage: low

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final validation — Phase 1 + Phase 2

Static verification of b619a4f found no actionable issues: enabling the existing re-sign mechanism preserves same-message vote safety and restores share delivery in both connection modes. The regression test exercises session expiration and recovery without changing the selected quorum, with both variants registered in the functional runner. Current CI shows all reported code checks passing, including ASan, TSan, and multiprocess tests; only the automated review remains pending, and no local builds or tests were run.

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: dash-core-commit-history); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: low by gpt-6.1-sol (effort low) — The diff makes a contained one-argument change to enable an existing re-signing path and adds focused regression coverage, without introducing intricate changes to signature handling or consensus rules.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort high); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity quota lookup failed), glm-5.3-flash (lane failed)
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort medium); agent phase2-reviewer, gpt-6.1-sol — dash-core-commit-history (completed, effort medium); agent phase2-reviewer

@thepastaclaw thepastaclaw added the pastaclaw:approved thepastaclaw's latest review approved this PR label Nov 12, 2026

@knst knst left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK b619a4f

@UdjinM6 UdjinM6 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should merge this, at least not now. The existing code already has retry points:

  • llmq_400_85 doesn't rotate. SelectQuorumForSigning scores the last 4 mined quorums against the fixed requestId, and a DKG runs every 576 blocks. The selected quorum changes when a new quorum scores better or when the selected one drops out of the active set, which happens within about 4 days if DKGs succeed.
  • A switch changes msgHash, because it includes quorumHash. allowDiffMsgHashSigning=true then lets eligible members of the new quorum sign. They have no votes recorded yet, they sign when they see the same tip, and SESSION_NEW_SHARES_TIMEOUT is an inactivity timeout that each new share extends. So every switch is a fresh, coordinated attempt.
  • Votes also expire after -maxrecsigsage (7 days by default).

The current quorum may well be unable to sign even if most of its members are now upgraded. That's because some of them already signed alone earlier, not because of how ready the network is. But the next selected quorum gets a clean attempt, and whether 85% of its members are upgraded and online together is exactly the masternode readiness check we want before v24 moves forward.

Mining the EHF signal doesn't activate v24 by itself, but it does start miner signalling and the 80%→60% threshold decay. The masternode signal is the only thing that controls when that clock starts. Re-signing on every block until the signal goes through would start it earlier, before masternodes have shown they are stable on the new version. The v24 window runs until Nov 2028, so waiting a few quorum switches costs us very little. I'd rather let this run its natural course.

@PastaPastaPasta

Copy link
Copy Markdown
Member Author

Agreed on the mechanics: a quorum switch gives a fresh attempt today (allowDiffMsgHashSigning is already true on this call), and votes expire after -maxrecsigsage. Two things from mainnet are worth having on the table before deciding, though.

No switch has happened, and none can until an llmq_400_85 DKG succeeds. At tip 2571074:

  • The newest llmq_400_85 quorum is the one based at 2568960. The DKGs based at 2569536, 2570112 and 2570688 mined no commitment.
  • The v24 window opened at about height 2569120 (median time past reaching the 2026-11-09 00:00 UTC start time), after that quorum was already mined.

So the active set is the same four quorums as when signing began, and the selected quorum has never changed. The retry-by-switch path depends on the DKG problem being fixed first (#8135, #8130), which itself needs most of the quorum's members on a new release.

The vote expiry is per node, counted from when that node signed. Members that signed when the window opened retry together around Nov 16, but the ones that upgraded later expire at staggered times and don't join them, so that isn't a clean attempt either.

This change doesn't lower the bar. Members still on 24.0.0 that already voted keep their "sign once" behaviour, so the signature only completes when 340 of the selected quorum's 400 members run the release with this change and are online for the same block. That is the same 85% check, measured against the newer release, and it can't go through at a lower level of readiness than a fresh quorum would require.

What it changes is when the check is sampled: on every block instead of only at a switch, so the signal no longer depends on DKGs succeeding or on which quorum happens to score best. Once DKGs work again, the difference in timing between the two paths is at most a few DKG cycles.

So the question is whether that independence from DKG health is worth taking in 24.0.1, or whether fixing the DKGs and letting the next switch do it is enough. Leaving that call to you and pasta.


🤖 Posted autonomously by Claude on behalf of pasta.

@UdjinM6

UdjinM6 commented Nov 12, 2026

Copy link
Copy Markdown

Thanks for the numbers. I agree that no switch has happened and that vote expiry is staggered, so neither is a clean retry right now.

I don't see independence from DKG health as a benefit, though. It's what EHF is supposed to depend on. The point of making a hard fork wait for a masternode quorum signature is that the network has to show it can run quorums reliably on the new version before activation starts. If llmq_400_85 DKGs can't produce a commitment, that's a readiness problem, and the EHF signal staying unsigned is the mechanism working as designed, not a bug to route around.

It's true that this change still needs 340 members on the new release, so it doesn't lower the adoption bar. What it removes is the requirement that the network can actually form the quorums. A network that is 85% upgraded but can't complete a 400-member DKG isn't one I'd want to start miner signalling and the 80%→60% threshold decay on.

So I'd prefer to fix the DKGs first (#8135/#8130 in 24.0.1) and let a quorum from a successful DKG produce the signal. By your own estimate that costs at most a few DKG cycles once the fix is out, and it means the signal comes from a network that has shown it's healthy.

@PastaPastaPasta

Copy link
Copy Markdown
Member Author

I agree fixing the DKG is more important part of this; but to me EHF's job isn't to "check that new quorums are happening"; the purpose is simply did 85% of MNs upgrade. It feels very weird to have 3-7 days (probabilistically) of lag between the network has upgraded enough, and we finally get signal b/c we need it to be from a fresh quorum. I guess that's fine, and we can do 24.0.1 w/ just the dkg cache fixes, but seems strange

@PastaPastaPasta

Copy link
Copy Markdown
Member Author

Decision from pasta: v24.0.1 goes ahead without this. The pick has been removed from #8128 and the release notes in #8133 now describe the signal being attempted again when a newly formed quorum is selected.

Leaving this PR open for a later decision on develop; it no longer blocks the release.


🤖 Posted autonomously by Claude on behalf of pasta.

@UdjinM6

UdjinM6 commented Nov 12, 2026

Copy link
Copy Markdown

I don't think the lag after DKGs recover is that long.

Each successful llmq_400_85 DKG adds one quorum and pushes the oldest of the 4 out, so the selected quorum changes either when a new one scores better or when the selected one is pushed out. Depending on where the selected quorum sits in the set, that takes 1 to 4 successful DKGs. A new quorum beats the incumbent with odds of 1/5, 1/6 and 1/7, because the incumbent already won among 4, so the expected wait is about 2 successful DKGs. In blocks, counted from the base of the first successful DKG and assuming they're consecutive, that's 28 to 1784 blocks: mining offset 20-48 plus SIGN_HEIGHT_OFFSET. quorum list and quorum selectquorum 3 c474f10ec3ff41beef91da44bce948772a816814c0f6d64d1bb23c9cc182fe1b show where the selected quorum sits now, which gives the upper bound.

Also, the switch doesn't need a newly formed quorum. If the selected quorum is pushed out, selection can move to an older quorum still in the set. Its msgHash is different because it includes quorumHash, so with allowDiffMsgHashSigning=true that's a fresh attempt too.

@PastaPastaPasta

Copy link
Copy Markdown
Member Author

Ran that against mainnet at tip 2571168. The selected quorum for that request is 00000000000000287fd0a217482410c9e7d5700d0c3e89f0ca781c7931b4f8a7, based at 2568960, which is the newest of the four (the others are based at 2568384, 2567808 and 2567232).

So it sits in the worst position: it is only pushed out by the fourth successful DKG. With your odds, the switch comes at the first, second or third new quorum with probability 1/5, 2/15 and 2/21 (3/7 in total), and at the fourth otherwise (4/7). That is an expected wait of about 3 successful DKGs and at most 4, so roughly 3 to 4 days after DKGs recover if they succeed consecutively.

Agreed on the second point: a switch to an older quorum is a fresh attempt as well.


🤖 Posted autonomously by Claude on behalf of pasta.

@PastaPastaPasta

Copy link
Copy Markdown
Member Author

OK; I think we can close this for now.

@thepastaclaw thepastaclaw removed the pastaclaw:approved thepastaclaw's latest review approved this PR label Nov 12, 2026
@UdjinM6 UdjinM6 removed this from the 24.1 milestone Nov 12, 2026
PastaPastaPasta added a commit that referenced this pull request Nov 12, 2026
78ad074 Merge #8131: fix: wait for a matching superblock trigger before voting NO on the others (merge-script)
6faa5ce Merge #8097: fix: keep governance triggers across a restart on test networks (pasta)
2757e83 Merge #8130: fix: process a node's own DKG messages ahead of queued peer messages (merge-script)
cbbee0e Merge #8135: perf: keep verified DKG contributions cached through the commit phase (merge-script)
29a3cdd Merge #8099: fix(rest): don't terminate on blocks with undecodable ProTx payloads (merge-script)
275a142 Merge #8100: fix: don't abort in governance when a reorg changes masternode internal ids (merge-script)
d00c4e6 Merge #8104: fix: keep final commitments mineable when their block is rejected (merge-script)
b051d3a Merge #8080: test(qt): stop address book tests from answering the shared masternode wizard's prompt (merge-script)
ec9e944 Merge #8094: fix(net): stop busy-looping while a peer's send buffer is full (merge-script)
033dae3 Merge #8052: fix(qt): reserve the same shared masternode contribution again after a release (merge-script)
65603d5 Merge #8088: fix: fetch miniupnpc sources from miniupnp.free.fr (merge-script)

Pull request description:

  ## Issue being fixed or feature implemented

  Backports for v24.0.1, on top of `v24.0.x` at 938fba5 (v24.0.0). v24.0.0 is running on mainnet ahead of the V24 hard fork, and a handful of fixes that landed on develop since the release should not wait for 24.1: a `test_dash-qt` failure that blocks distribution packagers (#8075), a broken dependency download, a peer-triggerable busy loop, a way for a quorum commitment to be lost for a whole DKG cycle, and a node abort after a reorg.

  It also carries #8131, which changes when masternodes vote NO on superblock triggers. Inside a superblock's maturity window a masternode votes NO on every trigger that doesn't match its candidate and never revisits that vote. If the approved set changes while triggers are being created, masternodes can end up having voted NO on the trigger that matches their final candidate, and the superblock can go unpaid. With #8131 a masternode waits for a trigger matching its own candidate before it votes NO on the others (until 3 blocks before the superblock), and clears the per-cycle "voted YES" marker by height so an unfunded cycle doesn't carry over into the next one. This was a separate PR (#8132) until #8131 merged on develop; it is folded in here so the release branch gets one backport PR.

  It also carries the fixes for the `llmq_400_85` DKG rounds that have been failing on mainnet, which is what the stalled v24 activation is waiting on: #8135, which stops DKG members from reloading every verification vector at commit time, the reason slow members have been missing the commit phase of `llmq_400_85` rounds, and #8130, which makes a member relay its own DKG messages before it works through queued peer messages.

  ## What was done?

  ### Picked

  All eleven are picked from their develop merge commits with `git cherry-pick -m1 -x`. Each pick changes the same lines as its develop counterpart except where noted.

  - #8088 fix: fetch miniupnpc sources from miniupnp.free.fr
  - #8052 fix(qt): reserve the same shared masternode contribution again after a release
  - #8094 fix(net): stop busy-looping while a peer's send buffer is full
  - #8080 test(qt): stop address book tests from answering the shared masternode wizard's prompt (fixes #8075)
  - #8104 fix: keep final commitments mineable when their block is rejected, with the adaptation below
  - #8100 fix: don't abort in governance when a reorg changes masternode internal ids, with the adaptation below
  - #8099 fix(rest): don't terminate on blocks with undecodable ProTx payloads
  - #8135 perf: keep verified DKG contributions cached through the commit phase, with the adaptation below
  - #8130 fix: process a node's own DKG messages ahead of queued peer messages
  - #8097 fix: keep governance triggers across a restart on test networks, source change only (see below)
  - #8131 fix: wait for a matching superblock trigger before voting NO on the others

  ### Adaptations

  - **#8104:** `v24.0.x` doesn't have #7936 (parse each quorum commitment payload once per block). `src/llmq/blockprocessor.cpp` keeps the `v24.0.x` `GetCommitmentsFromBlock` and adds the new `RemoveMineableCommitmentsForBlock()` unchanged; in `blockprocessor.h` its lock annotation drops `!m_commitment_payload_mutex`, a mutex that only exists with #7936. `Chainstate` has no `m_special_tx` member on `v24.0.x`, so the two new calls in `validation.cpp` go through `m_chain_helper->special_tx`, like the neighbouring special-transaction calls. The `specialtxman.*` hunks are identical to the develop merge.
  - **#8135:** in `CDKGSessionManager::CleanupCache`, `v24.0.x` still has a hand-written erase loop that develop had already replaced with `std::erase_if`; the pick takes the new `std::erase_if` form. The benchmark added in `src/bench/bls_dkg.cpp` is left out: it conflicts with the `v24.0.x` version of that file and isn't needed on the release branch. The rest of the source change is identical.
  - **#8097:** only the source change is picked (two lines in `src/governance/governance.cpp`). It is needed for #8131's test: without it a masternode restarted inside the maturity window drops its stored triggers on regtest, testnet and devnets and then rejects the superblock, which is what the restart case of that test does. Mainnet is not affected by that bug, because the expiry check involved is skipped there. #8097's own `feature_governance.py` step is left out because it restarts the pruned node added with #7860, which `v24.0.x` doesn't have.
  - **#8131:** no conflicts; `src/governance/signing.cpp` on `v24.0.x` is identical to develop before the change.
  - **#8100:** the test fixture in `src/test/governance_vote_processing_tests.cpp` keeps `CBaseChainParams::REGTEST`, since `v24.0.x` predates `ChainType`. The source change is identical.

  ### Not picked

  - #8129 (re-sign a pending EHF signal on every block): an earlier revision of this PR carried it. It is still under discussion on develop, and v24.0.1 goes ahead without it: the EHF signal is attempted again when a newly formed quorum is selected for it, which the DKG fixes here are meant to make possible.
  - #8101 (reject InstantSend locks with a noncanonical version or cycle hash): an earlier revision of this PR carried it. It scores a peer 100 for relaying a lock with a non-current version, but nodes without the change accept and relay such a lock, so in a mixed network an upgraded node would discourage honest peers that are still on v24.0.0. It needs to stop scoring that case before it ships anywhere.
  - #8095 and its follow-up #8121 (block templates while the best ChainLock is above the tip): with both, a node refuses to build a template in that window, which is what v24.0.0 already does with a less helpful error. An earlier revision of this PR carried #8095 alone; it was dropped once #8121 merged.
  - #8102 (quorum scan cache): written on top of the #7956 refactor; it needs a hand-port to two functions on `v24.0.x`.
  - #8116 and #7815 (CoinJoin coordinator): long-standing, mixing only; #8116's test depends on #7815.
  - #8085, #8096, #8098, #8103, and the features, refactors, Bitcoin Core backports, CI and test-only changes merged since the branch point.

  ## How Has This Been Tested?

  macOS arm64, depends build with `--enable-debug --enable-werror`, no GUI. The current head is 78ad074.

  On the current head:

  - `dashd` and `test_dash` build cleanly.
  - Unit suites `governance*`, `llmq*`, `dkg*`, `evo*` and `versionbits*` pass, on the relic-based BLS library this branch uses.
  - `feature_governance_trigger_voting.py`, `feature_governance.py`, `feature_governance_cl.py`, `feature_governance_objects.py`, `feature_llmq_dkgerrors.py`, `feature_mnehf.py` and `interface_rest.py` pass.

  On earlier revisions of this branch. The current head has the same content as those revisions for the picks concerned; since then #8129 was dropped (one line in `src/llmq/ehf_signals.cpp` plus its test changes), the #8097 and #8131 picks were added, and the later picks were redone from their develop merge commits without changing the tree:

  - `feature_llmq_signing.py` (both variants), `feature_llmq_chainlocks.py` and `feature_llmq_rotation.py` pass (on the revision without #8097 and #8131).
  - Without the #8097 source change, `feature_governance_trigger_voting.py` fails in its restart case: the restarted masternode rejects the superblock with `bad-cb-amount`.
  - Unit suites `bls*` pass. On a revision that also carried #8095 and #8101, `coinjoin*`, `net_tests`, `miner_tests` and `validation*` passed as well.
  - The full functional suite (`test_runner.py -j16`), on the revision that still carried #8129 and not yet #8097 and #8131: 307 passed, 17 skipped, 1 failed. The skipped ones are the usual environment-dependent tests (`*_bind*`, `interface_usdt_*`, `interface_zmq*`, previous-release compatibility and `feature_unsupported_utxo_db.py`). The run includes `feature_llmq_dkgerrors.py` with #8104's new scenario and `interface_rest.py` with #8099's.
  - The failure was `feature_llmq_signing.py`, in its first `mine_quorum`, waiting for every member to have received all contributions. The node logs show one masternode relaying its contribution about 0.2 s before its connections to the other members had been authenticated, so no peer received it. That happens before anything #8135 or #8130 changes, and it is load-dependent: running four copies of the test in both variants at `-j8`, three times, 2 of 24 runs fail on that revision and 2 of 24 fail on the same branch without the two DKG commits, with the same assertion.
  - A full functional run with the #8097 and #8131 changes on top of an older revision of this branch: 310 passed, 17 skipped, none failed.

  #8131 describes its scenarios and how each was shown to fail without the change.

  The GUI picks (#8052, #8080) were not built or run locally; CI covers them. The first push of this branch did not compile (`m_special_tx`, see Adaptations); review caught it and it was fixed before any of the above was run.

  ## Breaking Changes

  None. Behaviour change for masternodes (#8131): a NO vote on a non-matching trigger may be cast later than before, when the masternode's own candidate has no trigger yet, and at the latest 3 blocks before the superblock.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [ ] I have made corresponding changes to the documentation
  - [x] I have assigned this pull request to a milestone _(if you have permission to set one)_

  🤖 Generated with [Claude Code](https://claude.com/claude-code)

Top commit has no ACKs.

Tree-SHA512: 8ce635d02dd2b9b285aa6a1630d329ffeb9dcb99947a7a758c80ed17a2dbeb7496162a7f3052e872f46d481c6e6391ce866fd296d3ef1fa8edb1dc2d2c5f275e
@thephez

thephez commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator

Mining the EHF signal doesn't activate v24 by itself, but it does start miner signalling and the 80%→60% threshold decay. The masternode signal is the only thing that controls when that clock starts. Re-signing on every block until the signal goes through would start it earlier, before masternodes have shown they are stable on the new version. The v24 window runs until Nov 2028, so waiting a few quorum switches costs us very little. I'd rather let this run its natural course.

I agree with this. Hard forks don't need to be an urgent thing (unless it's due to an emergency). Delaying even a week or two isn't concerning to me. Not everything needs to operate at the speed of AI 🤷‍♂️

Just my 2 cents 🙂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants